#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2020/5/30 17:38
# @Author  : Tang Yiwei
# @Email   : yiweitang@yunify.com
# @File    : date_util.py
# @Software: PyCharm

import calendar
import datetime
import math
import os
import time

ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
ISO8601_MS = "%Y-%m-%dT%H:%M:%S.%fZ"


def exec_time(start_time, end_time):
    """
    返回两个时间戳的时间差
    :param start_time:
    :param end_time:
    :return:
    """
    return "{:.2f} s".format(end_time - start_time)


def calculate_function_run_time(func, unit="s"):
    """
    计算函数的运行时间—单位s,传递ms，则打印毫秒
    :param unit:
    :param func: 被装饰的函数
    :return:None
    """

    def call_fun(*args, **kwargs):
        start_time = time.time()
        f = func(*args, **kwargs)
        end_time = time.time()
        if unit == "s":
            print("%s() run time：%s s" % (func.__name__, int(end_time - start_time)))
        else:
            print(
                "%s() run time：%s ms"
                % (func.__name__, int(1000 * (end_time - start_time)))
            )
        return f

    return call_fun


def get_ts(ts=None):
    """
    获取当前UTC时间
    :param ts:
    :return: example->2022-06-13T04:41:55Z
    """
    if not ts:
        ts = time.gmtime()
    return time.strftime(ISO8601, ts)


def parse_ts(ts):
    """
    返回时间戳
    :param ts:
    :return:
    """
    ts = ts.strip()
    try:
        ts_s = time.strptime(ts, ISO8601)
        return time.mktime(ts_s)
    except ValueError:
        try:
            ts_s = time.strptime(ts, ISO8601_MS)
            return time.mktime(ts_s)
        except ValueError:
            return 0


def local_ts(utc_ts):
    """
    UTC时间转时间戳
    :param utc_ts:2022-05-31T16:00:00.000Z  ->   1653994800.0
    :return:
    """
    ts = parse_ts(utc_ts)
    if ts:
        return ts - (-10800)
    else:
        return 0


def get_current_time(format=True):
    """
    获取系统本地时间
    :format:example:%H_%M_%S_%f
    :return:example:12点48分24秒->12_48_24
    """
    if format:
        return datetime.datetime.now().strftime(ISO8601)
    else:
        return datetime.datetime.now()


def get_current_utc_time(format=True):
    """
    获取UTC世界统一时间
    :return:
    """
    if format:
        return datetime.datetime.utcnow().strftime(ISO8601)
    else:
        return datetime.datetime.utcnow()


def get_timestamp(t, fm="%Y-%m-%d_%H:%M:%S"):
    return int(time.mktime(time.strptime(t, fm)))


def get_delay_time(interval="hours", delay=0, utc=True, format=ISO8601):
    """
    根据当前世界UTC时间或者本地时间来指定获取之后的某个单位的时间
    :param interval: 单位为：天days、秒seconds、微秒microseconds、毫秒milliseconds、分minutes、小时hours、周weeks
    :param delta: int类型的正数或者负数，正数代表之后的某个时间，负数代表之前的某个时间
    :param utc: 选择采用本地时间还是采用世界UTC时间,默认采用世界UTC时间
    :param format: 默认的格式采用ISO8601
    :return:get_delay_time(interval='minutes',delay=20,utc=True) -> ('2022-08-24T10:58:46Z', '2022-08-24T11:18:46Z')
    """
    if utc:
        now_time = datetime.datetime.utcnow()
    else:
        now_time = datetime.datetime.now()
    if interval == "hours":
        delta_time = (now_time + datetime.timedelta(hours=delay)).strftime(format)
    elif interval == "days":
        delta_time = (now_time + datetime.timedelta(days=delay)).strftime(format)
    elif interval == "weeks":
        delta_time = (now_time + datetime.timedelta(weeks=delay)).strftime(format)
    elif interval == "seconds":
        delta_time = (now_time + datetime.timedelta(seconds=delay)).strftime(format)
    elif interval == "microseconds":
        delta_time = (now_time + datetime.timedelta(microseconds=delay)).strftime(
            format
        )
    elif interval == "milliseconds":
        delta_time = (now_time + datetime.timedelta(milliseconds=delay)).strftime(
            format
        )
    elif interval == "minutes":
        delta_time = (now_time + datetime.timedelta(minutes=delay)).strftime(format)
    else:
        raise ValueError("interval error")
    return now_time.strftime(format), delta_time


def get_delay_time_before(interval="hours", delay=0, utc=True, format=ISO8601):
    """
    根据当前世界UTC时间或者本地时间来指定获取之前的某个单位的时间
    :param interval: 单位为：天days、秒seconds、微秒microseconds、毫秒milliseconds、分minutes、小时hours、周weeks
    :param delta: int类型的正数或者负数，正数代表之后的某个时间，负数代表之前的某个时间
    :param utc: 选择采用本地时间还是采用世界UTC时间,默认采用世界UTC时间
    :param format: 默认的格式采用ISO8601
    :return:get_delay_time(interval='minutes',delay=20,utc=True) -> ('2022-08-24T10:58:46Z', '2022-08-24T11:18:46Z')
    """
    if utc:
        now_time = datetime.datetime.utcnow()
    else:
        now_time = datetime.datetime.now()
    if interval == "hours":
        delta_time = (now_time - datetime.timedelta(hours=delay)).strftime(format)
    elif interval == "days":
        delta_time = (now_time - datetime.timedelta(days=delay)).strftime(format)
    elif interval == "weeks":
        delta_time = (now_time - datetime.timedelta(weeks=delay)).strftime(format)
    elif interval == "seconds":
        delta_time = (now_time - datetime.timedelta(seconds=delay)).strftime(format)
    elif interval == "microseconds":
        delta_time = (now_time - datetime.timedelta(microseconds=delay)).strftime(
            format
        )
    elif interval == "milliseconds":
        delta_time = (now_time - datetime.timedelta(milliseconds=delay)).strftime(
            format
        )
    elif interval == "minutes":
        delta_time = (now_time - datetime.timedelta(minutes=delay)).strftime(format)
    else:
        raise ValueError("interval error")
    return now_time.strftime(format), delta_time


def get_current_date():
    """
    获取当前的年-月-日
    :return:example:->2022-6-13
    """
    timeTup = time.localtime()
    currentDate = (
        str(timeTup.tm_year) + "-" + str(timeTup.tm_mon) + "-" + str(timeTup.tm_mday)
    )
    return currentDate


def get_stamp(unit="s"):
    """
    返回当前unix时间戳
    :param unit:单位为s or ms
    :return:s->1655095947.66132  ms->1655095947661
    """
    if unit == "s":
        return time.time()
    if unit == "ms":
        return round(time.time() * 1000)


def time_stamp_to_datetime(time_stamp, format="%Y-%m-%d %H:%M:%S"):
    """
    时间戳转换为日期时间格式
    :param time_stamp:example-> 1655096074: 2022-06-13 12:54:34
    :return:
    """
    return time.strftime(format, time.localtime(int(time_stamp)))


def get_local_time():
    """
    获取当前时间
    :return:example->time.struct_time(tm_year=2022, tm_mon=6, tm_mday=13, tm_hour=13, tm_min=0, tm_sec=18, tm_wday=0, tm_yday=164, tm_isdst=0)
    """
    return time.localtime(time.time())


def get_local_stamp():
    """
    将本地时间转化为时间戳,example->1655096452.0
    :return:
    """
    return time.mktime(time.localtime())


def get_local_date():
    """
    获取当前的年-月-日 example:2022-6-13
    :return:
    """
    timeTup = time.localtime()
    currentDate = (
        str(timeTup.tm_year) + "-" + str(timeTup.tm_mon) + "-" + str(timeTup.tm_mday)
    )
    return currentDate


def get_local_date_and_now():
    """
    获取当前本地的日期和时间，例如:2020-05-31 16:34:34
    :return:
    """
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())


def get_iso_format_time():
    """
    获取iso格式的时间 ->2022-06-13T13:05:36.887720
    :return:
    """
    return datetime.datetime.now().isoformat()


def get_strptime(param):
    """
    将字符串转化为结构化时间
    :param param: 例如：2022-06-13 13:06:05 ->time.struct_time(tm_year=2022, tm_mon=6, tm_mday=13, tm_hour=13, tm_min=6, tm_sec=5, tm_wday=0, tm_yday=164, tm_isdst=-1)
    :return:
    """
    return time.strptime(param, "%Y-%m-%d %H:%M:%S")


def get_asctime():
    """
    获取格式化的时间,example:Mon Jun 13 13:06:51 2022
    :return:
    """
    return time.asctime(time.localtime(time.time()))


def format_timer(allTime):
    """
    秒转成格式化时间
    :param allTime:sec
    :return:
    eg:1d:3h:46m:40s
    2h:46m:40s
    1m:40s
    59s
    """
    day = 24 * 60 * 60
    hour = 60 * 60
    min = 60
    if allTime < 60:
        return f"{math.ceil(allTime)}s"
    elif allTime > day:
        days = divmod(allTime, day)
        return f"{int(days[0])}d:{format_timer(days[1])}"
    elif allTime > hour:
        hours = divmod(allTime, hour)
        return f"{int(hours[0])}h:{format_timer(hours[1])}"
    else:
        mins = divmod(allTime, min)
        return f"{int(mins[0])}m:{math.ceil(mins[1])}s"


def get_is_leap(year):
    """
    判断是否是闰年
    :param year:年份
    :return:True or False
    """
    return calendar.isleap(year)


def get_leap_num(y1, y2):
    """
    获取两个年份之间闰年的总数
    :param y1:
    :param y2:
    :return:
    """
    return calendar.leapdays(y1, y2)


def get_month_range(year, month):
    """
    获取该月的第一天是新期几和该月共计多少天
    :param year:
    :param month:
    :return:
    """
    return calendar.monthrange(year, month)


def get_weekday(year, month, day):
    """
    获取星期码
    :param year:
    :param month:
    :param day:
    :return: 0-6
    """
    return calendar.weekday(year, month, day)


def create_dir(dir):
    """
    在给定的路径下创建年-月-日类型的文件夹
    :param dir:
    :return:目录名称，例如:2021-2-18
    """
    dirName = os.path.join(dir, get_current_date())
    if not os.path.exists(dirName):
        os.makedirs(dirName)
    return dirName


def get_timestamp_iso():
    """
    获取iso格式的时间
    :return:
    """
    now = int(time.time())
    time_stamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(now))
    return time_stamp


def get_yesterday():
    """
    获取昨天日期
    :return: 今天的头一天的日期
    """
    now_time = datetime.datetime.now()
    yesterday = (now_time + datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
    return yesterday


def get_nday_list(n):
    """
    获取过去 N 天的日期
    :param n:
    :return:
    """
    before_n_days = []
    for i in range(1, n + 1)[::-1]:
        before_n_days.append(str(datetime.date.today() - datetime.timedelta(days=i)))
    return before_n_days


def create_assist_date(datestart=None, dateend=None):
    """
    生成一段时间区间内的日期
    :param datestart:开始日期
    :param dateend:结束日期
    :return:
    """
    # 创建日期辅助表
    if datestart is None:
        datestart = "2022-01-01"
    if dateend is None:
        dateend = datetime.datetime.now().strftime("%Y-%m-%d")

    # 转为日期格式
    datestart = datetime.datetime.strptime(datestart, "%Y-%m-%d")
    dateend = datetime.datetime.strptime(dateend, "%Y-%m-%d")
    date_list = []
    date_list.append(datestart.strftime("%Y-%m-%d"))
    while datestart < dateend:
        # 日期叠加一天
        datestart += datetime.timedelta(days=+1)
        # 日期转字符串存入列表
        date_list.append(datestart.strftime("%Y-%m-%d"))
    return date_list


def get_now_data_time(flag=0):
    """
    获取时间和日期
    flag = 0为时间和日期         eg:2018-04-11 10:04:55
    flag = 1仅获取日期           eg:2018-04-11
    flag = 2仅获取时间           eg:10:04:55
    flag = 3纯数字的日期和时间    eg:20180411100455
    """
    now = time.localtime(time.time())
    if flag == 0:
        return time.strftime("%Y-%m-%d %H:%M:%S", now)
    if flag == 1:
        return time.strftime("%Y-%m-%d", now)
    if flag == 2:
        return time.strftime("%H:%M:%S", now)
    if flag == 3:
        return time.strftime("%Y%m%d%H%M%S", now)


def get_current_month_start_and_end(date):
    """
    获取本月的第一天和最后一天的日期,example:get_current_month_start_and_end('2019-11-28')
    :param date:
    :return:本月第一天日期和本月最后一天日期
    """
    if date.count("-") != 2:
        raise ValueError("- is error")
    year, month = str(date).split("-")[0], str(date).split("-")[1]
    end = calendar.monthrange(int(year), int(month))[1]
    start_date = "%s-%s-01" % (year, month)
    end_date = "%s-%s-%s" % (year, month, end)
    return start_date, end_date


def utc_to_local(utc_time):
    """
    UTC时间转换为本地时间
    :utc_time:UTC时间字符串,example:2022-05-31T16:00:00.000Z
    :return: 2022-06-01 00:00:00
    """
    UTC_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
    utc_time = datetime.datetime.strptime(utc_time, UTC_FORMAT)
    local_time = utc_time + datetime.timedelta(hours=8)
    return local_time


def get_datetime_range(year_month, to_timestamp=False):
    """
    获取给定月份的开始时间和结束时间
    :params: year_month: '2022-07'
    :return: (datetime.datetime(2022, 7, 1, 0, 0), datetime.datetime(2022, 7, 31, 23, 59, 59, 999999)) or (1656604800, 1659283199)
    """
    from datetime import date, datetime

    year, month = year_month.split("-")
    year = int(year)
    month = int(month)
    start_time = datetime.combine(date(year, month, 1), datetime.min.time())
    _end_date = date(year, month, calendar.monthrange(year, month)[1])
    end_time = datetime.combine(_end_date, datetime.max.time())
    if to_timestamp:
        start_time = int(time.mktime(start_time.timetuple()))
        end_time = int(time.mktime(end_time.timetuple()))
    return start_time, end_time


def iterate_months(start_year_month, end_year_month):
    """
    迭代两个月份之间的月份
    :params: start_year_month: '2021-7'
    :params: end_year_month: '2022-08'
    :return: >>> list(iterate_months('2021-7', '2022-08'))
    ['2021-07', '2021-08', '2021-09', '2021-10', ..., '2022-07', '2022-08']
    """
    assert start_year_month <= end_year_month, "开始月份必须小于等于结束月份"
    year, month = start_year_month.split("-")
    year = int(year)
    month = int(month)

    end_year, end_month = end_year_month.split("-")
    end_year = int(end_year)
    end_month = int(end_month)
    assert month <= 12 and end_month <= 12, "月份必须在 1 - 12月之间"

    while True:
        yield "{}-{:02}".format(year, month)
        if year == end_year and month == end_month:
            break
        else:
            month = ((month + 1) % 12) or 12
            if month == 1:
                year += 1
